﻿using System;
using System.Collections.Generic;
using System.Linq;
using Utils.Wpf;

namespace StockAnalyst2.StockGrid
{
	internal class AllColumnsOptionsVM : ViewModelValidatedBase, IDisposable
	{
		#region Attributes

		private readonly List<ColumnOptionsVM> _columns;

		#endregion

		#region Properties

		public AllColumnsOptions Model { get; private set; }

		public ColumnOptionsVM Symbol { get; set; }
		public ColumnOptionsVM Value { get; set; }
		public ColumnOptionsVM OpenDiffPercentage { get; set; }
		public ColumnOptionsVM PreviousDiffPercentage { get; set; }
		public ColumnOptionsVM BidVolume { get; set; }
		public ColumnOptionsVM Bid { get; set; }
		public ColumnOptionsVM Ask { get; set; }
		public ColumnOptionsVM AskVolume { get; set; }
		public ColumnOptionsVM GapDiffPercentage { get; set; }
		public ColumnOptionsVM BidAskAverage { get; set; }
		public ColumnOptionsVM Max { get; set; }
		public ColumnOptionsVM Min { get; set; }
		public ColumnOptionsVM LastVolume { get; set; }
		public ColumnOptionsVM TotalVolume { get; set; }
		public ColumnOptionsVM LastOperation { get; set; }
		public ColumnOptionsVM MaxOpenDiffPercentage { get; set; }
		public ColumnOptionsVM MinOpenDiffPercentage { get; set; }
		public ColumnOptionsVM OpenValue { get; set; }
		public ColumnOptionsVM PreviousDayValue { get; set; }
		public ColumnOptionsVM Status { get; set; }
		public ColumnOptionsVM Date { get; set; }

		#endregion

		#region Public Methods

		public AllColumnsOptionsVM(AllColumnsOptions model)
		{
			this.Model = model;

			_columns = new List<ColumnOptionsVM>();

			this.Symbol = new ColumnOptionsVM(this.Model.Symbol);
			this.Value = new ColumnOptionsVM(this.Model.Value);
			this.OpenDiffPercentage = new ColumnOptionsVM(this.Model.OpenDiffPercentage);
			this.PreviousDiffPercentage = new ColumnOptionsVM(this.Model.PreviousDiffPercentage);
			this.BidVolume = new ColumnOptionsVM(this.Model.BidVolume);
			this.Bid = new ColumnOptionsVM(this.Model.Bid);
			this.Ask = new ColumnOptionsVM(this.Model.Ask);
			this.AskVolume = new ColumnOptionsVM(this.Model.AskVolume);
			this.GapDiffPercentage = new ColumnOptionsVM(this.Model.GapDiffPercentage);
			this.BidAskAverage = new ColumnOptionsVM(this.Model.BidAskAverage);
			this.Max = new ColumnOptionsVM(this.Model.Max);
			this.Min = new ColumnOptionsVM(this.Model.Min);
			this.LastVolume = new ColumnOptionsVM(this.Model.LastVolume);
			this.TotalVolume = new ColumnOptionsVM(this.Model.TotalVolume);
			this.LastOperation = new ColumnOptionsVM(this.Model.LastOperation);
			this.MaxOpenDiffPercentage = new ColumnOptionsVM(this.Model.MaxOpenDiffPercentage);
			this.MinOpenDiffPercentage = new ColumnOptionsVM(this.Model.MinOpenDiffPercentage);
			this.OpenValue = new ColumnOptionsVM(this.Model.OpenValue);
			this.PreviousDayValue = new ColumnOptionsVM(this.Model.PreviousDayValue);
			this.Status = new ColumnOptionsVM(this.Model.Status);
			this.Date = new ColumnOptionsVM(this.Model.Date);

			this.InitializeColumn(this.Symbol);
			this.InitializeColumn(this.Value);
			this.InitializeColumn(this.OpenDiffPercentage);
			this.InitializeColumn(this.PreviousDiffPercentage);
			this.InitializeColumn(this.BidVolume);
			this.InitializeColumn(this.Bid);
			this.InitializeColumn(this.Ask);
			this.InitializeColumn(this.AskVolume);
			this.InitializeColumn(this.GapDiffPercentage);
			this.InitializeColumn(this.BidAskAverage);
			this.InitializeColumn(this.Max);
			this.InitializeColumn(this.Min);
			this.InitializeColumn(this.LastVolume);
			this.InitializeColumn(this.TotalVolume);
			this.InitializeColumn(this.LastOperation);
			this.InitializeColumn(this.MaxOpenDiffPercentage);
			this.InitializeColumn(this.MinOpenDiffPercentage);
			this.InitializeColumn(this.OpenValue);
			this.InitializeColumn(this.PreviousDayValue);
			this.InitializeColumn(this.Status);
			this.InitializeColumn(this.Date);
		}

		protected override string ValidateField(string fieldName)
		{
			string result = null;

			ColumnOptionsVM sourceColumn = _columns.First(c => c.Model.ColumnName == fieldName);
			List<ColumnOptionsVM> repeatedValueColumns = _columns.Where(c => c.DisplayIndex == sourceColumn.DisplayIndex && c != sourceColumn).ToList();

			if (repeatedValueColumns.Count != 0)
			{
				base.ValidatedErrors[fieldName] = true;
				result = "The value is repeated";

				foreach (ColumnOptionsVM repeatedValueColumn in repeatedValueColumns)
				{
					// To validate the other repeated field	only if it has not already an error
					if (base.ValidatedErrors.ContainsKey(repeatedValueColumn.Model.ColumnName) && !base.ValidatedErrors[repeatedValueColumn.Model.ColumnName])
					{
						base.OnPropertyChanged(repeatedValueColumn.Model.ColumnName);
					}
				}
			}
			else if (sourceColumn.DisplayIndex + 2 < 0 || sourceColumn.DisplayIndex - 2 > _columns.Count - 1)
			{
				base.ValidatedErrors[fieldName] = true;
				result = "The value is outside the valid range";
			}
			else
			{
				foreach (ColumnOptionsVM column in _columns)
				{
					if (base.ValidatedErrors.ContainsKey(column.Model.ColumnName) && base.ValidatedErrors[column.Model.ColumnName])
					{
						base.OnPropertyChanged(column.Model.ColumnName);
					}
				}
			}

			return result;
		}

		#endregion

		#region Event Handlers

		private void Any_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			ColumnOptionsVM columnOptionsVM = (ColumnOptionsVM)sender;
			base.OnPropertyChanged(columnOptionsVM.Model.ColumnName);
		}

		private void Any_ValidationRequested(object sender, ValidationRequestedEventArgs e)
		{
			ColumnOptionsVM columnOptionsVM = (ColumnOptionsVM)sender;
			e.ValidationResult = this[columnOptionsVM.Model.ColumnName];
		}

		#endregion

		#region Private Methods

		private void InitializeColumn(ColumnOptionsVM columnOptionsVM)
		{
			_columns.Add(columnOptionsVM);
			// The events should be catched and raised as own property changes to fire validation (ColumnOptionsVM cannot be validated by itself because it depends on others data)
			columnOptionsVM.PropertyChanged += this.Any_PropertyChanged; // Local object
			columnOptionsVM.ValidationRequested += this.Any_ValidationRequested; // Local object
		}

		#endregion

		#region IDisposable

		protected bool Disposed { get; private set; }

		private void Dispose(bool disposing)
		{
			if (!this.Disposed)
			{
				this.InternalDispose(disposing);
			}

			this.Disposed = true;
		}

		protected virtual void InternalDispose(bool disposing)
		{
			if (disposing)
			{
				this.Symbol.Dispose();
				this.Value.Dispose();
				this.OpenDiffPercentage.Dispose();
				this.PreviousDiffPercentage.Dispose();
				this.BidVolume.Dispose();
				this.Bid.Dispose();
				this.Ask.Dispose();
				this.AskVolume.Dispose();
				this.GapDiffPercentage.Dispose();
				this.BidAskAverage.Dispose();
				this.Max.Dispose();
				this.Min.Dispose();
				this.LastVolume.Dispose();
				this.TotalVolume.Dispose();
				this.LastOperation.Dispose();
				this.MaxOpenDiffPercentage.Dispose();
				this.MinOpenDiffPercentage.Dispose();
				this.OpenValue.Dispose();
				this.PreviousDayValue.Dispose();
				this.Status.Dispose();
				this.Date.Dispose();
			}

			// Dispose here only unmanaged objects 
			// Don’t use managed objects here because maybe 
			// they have been finalized already
		}

		public void Dispose()
		{
			this.Dispose(true);
			GC.SuppressFinalize(this);
		}

		~AllColumnsOptionsVM()
		{
			this.Dispose(false);
		}

		#endregion
	}
}
